package com.gframework.util;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 性能分析和线程安全测试方法.<br>
 * 这个类主要是用于开发阶段，你只需要在调用run方法，并传入一个方法函数，本类会重复调用多次，来取得一个执行时间信息。
 * 
 * @since 1.0.0
 * @author Ghwolf
 *
 */
public class G {
	/**
	 * 没有参数，没有返回值的函数接口
	 * @since 1.0.0
	 * @author Ghwolf
	 *
	 */
	@FunctionalInterface
	public static interface SimpleFunctionInterface {
		public void fun() ;
	}

	/**
	 * 执行次数
	 */
	static int times = 2147483;

	public static <T> void run(SimpleFunctionInterface fun, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.fun();
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}
	
	public static <T> void run(Function<T, ?> fun, T t, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.apply(t);
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}

	public static <T, U> void run(BiFunction<T, U, ?> fun, T t, U u, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.apply(t, u);
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}

	public static void run(Supplier<?> fun, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.get();
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}

	public static <T> void run(Consumer<T> fun, T t, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.accept(t);
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}

	public static <T, U> void run(BiConsumer<T, U> fun, T t, U u, int times) {
		long start = System.currentTimeMillis();
		long startNano = System.nanoTime();
		for (int x = 0; x < times; x++) {
			fun.accept(t, u);
		}
		long endNano = System.nanoTime();
		long end = System.currentTimeMillis();
		printInfo(end - start,endNano - startNano, times);
	}

	public static <T> void run(SimpleFunctionInterface fun) {
		run(fun, times);
	}
	
	public static <T> void run(Function<T, ?> fun, T t) {
		run(fun, t, times);
	}

	public static <T, U> void run(BiFunction<T, U, ?> fun, T t, U u) {
		run(fun, t, u, times);
	}

	public static void run(Supplier<?> fun) {
		run(fun, times);
	}

	public static <T> void run(Consumer<T> fun, T t) {
		run(fun, t, times);
	}

	public static <T, U> void run(BiConsumer<T, U> fun, T t, U u) {
		run(fun, t, u, times);
	}

	private static void printInfo(long time,long nanoTime, int times) {
		System.out.println("执行总时间： " + time + " 毫秒 ， " + nanoTime + " 纳秒");
		System.out.println("执行总次数： " + times + " 次");
		if (time < times) {
			if (nanoTime < times) {
				System.out.println("平均执行时间：<1 纳秒/次");
			} else {
				System.out.println("平均执行时间：" + Math.round((1.0 * nanoTime / times)) + " 纳秒/次");
			}
		} else {
			System.out.println("平均执行时间：" + Math.round((1.0 * time / times)) + " 毫秒/次");
		}
	}
	
	// thread safe test
	
	static class MyThread<T> implements Runnable {
		Supplier<T> supplier ;
		Predicate<T> predicate;
		public MyThread(Supplier<T> supplier,Predicate<T> predicate){
			this.supplier = supplier;
			this.predicate = predicate;
		}
		@Override
		public void run() {
			for (int i = 0 ; i < 100000 ; i ++) {
				T value = supplier.get();
				if (!predicate.test(value)) {
					System.out.println("【ERROR】异常值：" + value);
					System.exit(0);
				}
				
			}
		}
	}
	
	public static class TestThreadSafe<T> {
		class V {
			Supplier<T> supplier;
			Predicate<T> predicate;
			public V(Supplier<T> supplier,Predicate<T> predicate){
				this.supplier = supplier ;
				this.predicate = predicate ;
			}
		}
		List<V> vs = new ArrayList<>();
		
		TestThreadSafe(){
			
		}
		
		public TestThreadSafe<T> add(Supplier<T> supplier,Predicate<T> predicate) {
			vs.add(new V(supplier,predicate));
			return this;
		}
		public void start() {
			System.out.println("开始检测线程安全！");
			List<Thread> list = new ArrayList<>();
			for (int x = 0 ; x < 500 / vs.size() ; x ++) {
				try {
					Thread.sleep(1);
				} catch (Exception e) {
				}
				for (V v : vs) {
					Thread t = new Thread(new MyThread<>(v.supplier, v.predicate));
					list.add(t);
					t.start();
				}
				
			}
			for (Thread t : list) {
				try {
					t.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println("执行完毕，未检测出线程不安全，可以再次尝试以防止测试误差！");
		}
	}
	
	public static <T> TestThreadSafe<T> thread(Class<T> returnType) {
		return new TestThreadSafe<T>();
	}
	
	public static void main(String args[]) {
		DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		LocalDate d = LocalDate.now();
		LocalDate d2 = LocalDate.of(2019, 1, 1);
		LocalDate d3 = LocalDate.of(2009, 2, 2);
		String pre = sdf.format(d);
		String pre2 = sdf.format(d2);
		String pre3 = sdf.format(d3);
		thread(String.class)
			.add(() -> sdf.format(d),pre :: equals)
			.add(() -> sdf.format(d2),pre2 :: equals)
			.add(() -> sdf.format(d3),pre3 :: equals)
			.start();
	}

}
